Skip to content

Fix resume succeding prematurely#1573

Open
zealsham wants to merge 2 commits into
payjoin:masterfrom
zealsham:premature-resume
Open

Fix resume succeding prematurely#1573
zealsham wants to merge 2 commits into
payjoin:masterfrom
zealsham:premature-resume

Conversation

@zealsham
Copy link
Copy Markdown
Collaborator

@zealsham zealsham commented May 22, 2026

This resolves #1545, upon resuming a session where the sender posted the original proposal but went ofline, the cli succeds prematurely and outputs "payjoin transaction detected in mempool" because the cli treats any Ok(_) as sucess.

This also addresses a subtle issue where after a certain period without tx detected in the mempool the the monitor_proposal function returned
Err("Timeout waiting for payment confirmation after 5s").
That looked like a failure even though the session should stay open in Monitor. And finally the missleading "All resumed sessions completed" message.

my initial analysis of the issue in #1545 was wrong, as the intended solution witht that did not fix it. i had LLM (gemini) help with some of the root cause analysis

Pull Request Checklist

Please confirm the following before requesting review:

@zealsham zealsham force-pushed the premature-resume branch from 2303b41 to 56c9bb2 Compare May 22, 2026 14:19
@coveralls
Copy link
Copy Markdown
Collaborator

coveralls commented May 22, 2026

Coverage Report for CI Build 26640184155

Coverage increased (+0.05%) to 85.357%

Details

  • Coverage increased (+0.05%) from the base build.
  • Patch coverage: 7 uncovered changes across 1 file (18 of 25 lines covered, 72.0%).
  • No coverage regressions found.

Uncovered Changes

File Changed Covered %
payjoin-cli/src/app/v2/mod.rs 25 18 72.0%

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 13856
Covered Lines: 11827
Line Coverage: 85.36%
Coverage Strength: 391.26 hits per line

💛 - Coveralls

Copy link
Copy Markdown
Collaborator

@xstoicunicornx xstoicunicornx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like how you are breaking out session level information but I think this solution is a bit over engineered and it seems unnecessary to introduce additional state.

My comments below are a bit general but feel free to take more concrete direction from my branch https://github.com/xstoicunicornx/rust-payjoin/tree/pr1573-alt

Comment thread payjoin-cli/src/app/v2/mod.rs Outdated
Comment thread payjoin-cli/src/app/v2/mod.rs Outdated
Comment thread payjoin-cli/src/app/v2/mod.rs Outdated
@zealsham zealsham force-pushed the premature-resume branch 2 times, most recently from 0809f0c to 0e3690a Compare May 25, 2026 21:20
Copy link
Copy Markdown
Collaborator

@xstoicunicornx xstoicunicornx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit more feedback.

Comment thread payjoin-cli/src/app/v2/mod.rs Outdated
} => {
println!("All resumed sessions completed.");
let closed = results.iter().filter(|r| matches!(r, Ok(Ok(())))).count();
let total = spawned_recv_ids.len() + spawned_send_ids.len();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let total = spawned_recv_ids.len() + spawned_send_ids.len();
let total = results.len();

Why not just this? Then the spawned_recv_ids and spawned_send_ids aren't needed anymore.

Comment thread payjoin-cli/tests/e2e.rs Outdated
wait_for_stdout_match(&mut stdout, |line| {
line.contains("All resumed sessions completed.")
}),
wait_for_stdout_match(&mut stdout, |line| line.contains("Resume done:")),
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
wait_for_stdout_match(&mut stdout, |line| line.contains("Resume done:")),
wait_for_stdout_match(&mut stdout, |line| line.contains("1/1 session(s) closed.")),

This is supposed to assert that the resumed session was completed, checking for "Resume done:" only asserts that summary was printed not that the session was actually closed.

Comment thread payjoin-cli/src/app/v2/mod.rs Outdated
Err(_) => {
let id = persister.session_id();
println!(
"No payjoin transaction seen yet (waited {timeout_duration:?}). Session {id} \
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"No payjoin transaction seen yet (waited {timeout_duration:?}). Session {id} \
"No payjoin transaction seen yet, stopping (waited {timeout_duration:?} sec). Session {id} \

Comment thread payjoin-cli/src/app/v2/mod.rs Outdated
let id = persister.session_id();
println!(
"No payjoin transaction seen yet (waited {timeout_duration:?}). Session {id} \
remains open; run `payjoin-cli resume` to keep watching."
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
remains open; run `payjoin-cli resume` to keep watching."
remains open. Run `payjoin-cli resume` to keep watching."

nit: semicolon isn't used anywhere else

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for catching that

Comment thread payjoin-cli/src/app/v2/mod.rs Outdated
}
}
println!(
"Resume done: {closed}/{total} session(s) closed. \
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think printing the completed/total sessions count is useful or worth the complexity. It seems sufficient to print per-session results (session XYZ either completed, is still polling, or had an error).

Comment thread payjoin-cli/src/app/v2/mod.rs Outdated
Comment on lines +890 to +899
Err(_) => {
let id = persister.session_id();
println!(
"No payjoin transaction seen yet (waited {timeout_duration:?}). Session {id} \
remains open; run `payjoin-cli resume` to keep watching."
);
Err(anyhow!(
"No payjoin transaction detected in mempool within {timeout_duration:?}"
))
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this change seems unrelated to the resume fix, if really necessary it should be a separate commit.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes , it's not related to resume fix , more like a helpful ux . i'll extract to diff commit or drop it entirely

Comment thread payjoin-cli/src/app/v2/mod.rs
This addresses payjoin#1545, upon resuming a session where
the sender posted the original proposal but went ofline,
the cli succeds prematurely and outputs "payjoin transaction
detected in mempool" because the cli treats any Ok(_) as scucess.

This also addresses a subtle issue where after a certain period
without tx detected in the mempool the the monitor_proposal function returned
 Err("Timeout waiting for payment confirmation after 5s").
That looked like a failure even though the session should stay open in Monitor.
And finally the missleading "All resumed sessions completed" message.

Co-authored-by: xstoicunicornx <xstoicunicornx@users.noreply.github.com>
@zealsham zealsham force-pushed the premature-resume branch 2 times, most recently from 0ed9646 to 7779c42 Compare May 27, 2026 13:35
Copy link
Copy Markdown
Collaborator

@xstoicunicornx xstoicunicornx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comments below.

Comment thread payjoin-cli/src/app/v2/mod.rs Outdated
}

let mut tasks = Vec::new();
//let mut tasks = Vec::new();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
//let mut tasks = Vec::new();

Remove comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that was a mistake , thanks for catching it

Comment thread payjoin-cli/src/app/v2/mod.rs Outdated
Comment on lines 379 to 381
if all_completed {
println!("All resumed sessions completed.");
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since completion is being printed session by session already I think it would be better to just print a "Done" message here to indicate the resume has concluded.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry what I meant was that it should print "Done" regardless of whether all sessions were completed or not just to indicate that the resume command has finished. But tbh maybe we don't really need to print anything here anymore.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh!, i misunderstood you. Imho telling users that all resumed session completed is helpful . The "All resumed sessions completed" message can stay as it is and then we also have a diff message that indicates the Resume command is done. I also agree that not printing anything does not affect UX adversely.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would love to hear your opinion @xstoicunicornx before i push the new changes

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that every resumed session now prints whether the session completed or not, I feel like the user can already easily tell if all resumed session completed or not. So imo it doesn't convey any additional helpful information.

Comment thread payjoin-cli/src/app/v2/mod.rs Outdated
Comment on lines +901 to +905
let id = persister.session_id();
println!(
"No payjoin transaction seen yet, stopping (waited {timeout_duration:?} seconds). Session {id} \
remains open. run `payjoin-cli resume` to keep watching."
);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need this anymore now that errors are printed to the user upon task completion.

Comment thread payjoin-cli/src/app/v2/mod.rs Outdated
remains open. run `payjoin-cli resume` to keep watching."
);
Err(anyhow!(
"No payjoin transaction detected in mempool within {timeout_duration:?}"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"No payjoin transaction detected in mempool within {timeout_duration:?}"
"No payjoin transaction detected in mempool within {timeout_duration:?} sec, stopping."

I think this small change would make it much more clear what is happening.

Comment thread payjoin-cli/src/db/v2.rs Outdated
Comment on lines +144 to +145

pub fn session_id(&self) -> SessionId { self.session_id.clone() }
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be needed anymore.

Comment thread payjoin-cli/tests/e2e.rs Outdated

terminate(cli_resumer).await.expect("Failed to kill payjoin-cli");
assert!(res.is_some(), "Expected all resumed sessions completed");
assert!(res.is_some(), "Expected resume summary with all sessions closed");
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this line change is leftover from previous version of this PR.

@zealsham zealsham force-pushed the premature-resume branch from 7779c42 to 60a03aa Compare May 28, 2026 15:37
Copy link
Copy Markdown
Collaborator

@xstoicunicornx xstoicunicornx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Getting there :)

Comment thread payjoin-cli/src/app/v2/mod.rs Outdated
tasks.push(tokio::spawn(async move {
self_clone.process_sender_session(sender_state, &sender_persister).await
}));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: extra line added here

Comment thread payjoin-cli/src/app/v2/mod.rs Outdated
Comment on lines 379 to 381
if all_completed {
println!("All resumed sessions completed.");
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry what I meant was that it should print "Done" regardless of whether all sessions were completed or not just to indicate that the resume command has finished. But tbh maybe we don't really need to print anything here anymore.

Comment thread payjoin-cli/tests/e2e.rs Outdated
let timeout = tokio::time::Duration::from_secs(10);
let res = tokio::time::timeout(
timeout,
wait_for_stdout_match(&mut stdout, |line| line.contains("Done.")),
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
wait_for_stdout_match(&mut stdout, |line| line.contains("Done.")),
wait_for_stdout_match(&mut stdout, |line| {
line.starts_with("Session ") && line.ends_with(" completed."))
},

Better to key off of the print message that actually indicates the session was completed.

Comment thread payjoin-cli/tests/e2e.rs Outdated
Comment on lines +487 to +489
wait_for_stdout_match(&mut stdout, |line| {
line.contains("All resumed sessions completed.")
|| line.contains("No sessions to resume.")
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
wait_for_stdout_match(&mut stdout, |line| {
line.contains("All resumed sessions completed.")
|| line.contains("No sessions to resume.")
wait_for_stdout_match(&mut stdout, |line| {
line.starts_with("Session ") && line.ends_with(" completed.")

Same with this one. Plus not finding a string containing either of these no longer means that a session was not completed.

@zealsham zealsham force-pushed the premature-resume branch from 60a03aa to fb01450 Compare May 29, 2026 13:31
Copy link
Copy Markdown
Collaborator

@xstoicunicornx xstoicunicornx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good. Just one bad suggestion that I made that needs to be reverted (sorry... I didn't realize until I ran the test locally)

Comment thread payjoin-cli/src/app/v2/mod.rs Outdated
Err(_) => Err(anyhow!(
"Timeout waiting for payment confirmation after {:?}",
timeout_duration
"No payjoin transaction detected in mempool within {timeout_duration:?} seconds, stopping."
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"No payjoin transaction detected in mempool within {timeout_duration:?} seconds, stopping."
"No payjoin transaction detected in mempool within {timeout_duration:?}, stopping."

I was wrong about adding the "seconds", its already included as part of the timeout_duration.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also terrible that i missed it myself

@zealsham zealsham force-pushed the premature-resume branch from fb01450 to f3bc1ab Compare May 30, 2026 19:23
Copy link
Copy Markdown
Collaborator

@xstoicunicornx xstoicunicornx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK f3bc1ab

Solution is fairly minimal yet implements error handling and improves clarity of messages printed to the user. It also includes testing to guard against this bug in the future.

@xstoicunicornx
Copy link
Copy Markdown
Collaborator

xstoicunicornx commented May 30, 2026

@zealsham just updated your description to use the "resolves #issue-number" pattern which will automatically link this PR to that issue (and close that issue when this is merged).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

payjoin-cli resume succeeds prematurely

4 participants